package cn.tlb.gl.game.rebuild2;

import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.graphics.BitmapFactory;
import android.opengl.Matrix;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;

import javax.microedition.khronos.opengles.GL10;

/**
 * Created by 汤林冰 on 2016/1/13 15:06.
 * <p>
 *     类中的注释描述的左右方向是这样规定的:<br/>
 *     以人对着屏幕的视角看,你的左手即左边, 右手即右边
 * </p>
 */
public class GameModel implements SkinMappingIndex{
    /**人物头部尺寸, 以此作为标准, 头像纹理占8个像素,取值为1,由此计算比例*/
    public static final float HEAD_SIZE = .5f;
    public static final float unit = HEAD_SIZE/8;
    public static final float oz = 0;

    // body 8*4*12
    public static final float body_length = 4*unit;//  长/2
    public static final float body_width = 2*unit; //  宽/2
    public static final float body_height = 6*unit;//  高/2
    public static final float body_x = 0;
    public static final float body_y = 0;

    // head 8*8*8
    public static final float head_length = 4*unit;
    public static final float head_width = 4*unit;
    public static final float head_height = 4*unit;
    public static final float head_x = body_x;
    public static final float head_y = body_y + body_height + head_height;

    // 帽子和头部间距0.5像素
    public static final float cap_space = unit/2;
    public static final float cap_length = head_length+cap_space;
    public static final float cap_width = head_length+cap_space;
    public static final float cap_height = head_length+cap_space;
    public static final float cap_x = head_x;
    public static final float cap_y = head_y;

    // arm 4*4*12
    public static final float arm_length = 2*unit;
    public static final float arm_width = 2*unit;
    public static final float arm_height = 6*unit;
    public static final float left_arm_x = body_x - body_length - arm_width;
    public static final float left_arm_y = body_y;
    public static final float right_arm_x = -left_arm_x;
    public static final float right_arm_y = left_arm_y;

    //leg 4*4*12
    public static final float leg_length = 2*unit;
    public static final float leg_width = 2*unit;
    public static final float leg_height = 6*unit;
    public static final float left_leg_x = body_x - leg_width;
    public static final float left_leg_y = body_y - body_height - leg_height;
    public static final float right_leg_x = -left_leg_x;
    public static final float right_leg_y = left_leg_y;

    public static final Cuboid head = new Cuboid(head_x, head_y, oz, head_length, head_width, head_height);
    public static final Cuboid cap = new Cuboid(cap_x, cap_y, oz, cap_length, cap_width, cap_height);
    public static final Cuboid body = new Cuboid(body_x, body_y, oz, body_length, body_width, body_height);
    public static final Cuboid left_arm = new Cuboid(left_arm_x, left_arm_y, oz, arm_length, arm_width, arm_height);
    public static final Cuboid right_arm = new Cuboid(right_arm_x, right_arm_y, oz, arm_length, arm_width, arm_height);
    public static final Cuboid left_leg = new Cuboid(left_leg_x, left_leg_y, oz, leg_length, leg_width, leg_height);
    public static final Cuboid right_leg = new Cuboid(right_leg_x, right_leg_y, oz, leg_length, leg_width, leg_height);
    private static final float zOffSet = -2.5f;

    protected int[] textures;
    private ByteBuffer vertex_index = BufferUtil.wrap(new byte[]{0, 1, 2, 3, 4, 5});
    /**
     * 纹理坐标
     * <p>
     * 纹理坐标顺序(与顶点定义的顺序一致 如:{@link Cuboid#top}):
     * <pre>
     *              纹理坐标系
     *        O----------------> S
     *        |  1← ← ←3  5
     *        |  ↓    ↗ ↗ ↓
     *        |  ↓ ↗ ↗    ↓
     *        |   2  4← ← ←6
     *      T ↓
     * 所以纹理坐标:
     *      {
     *          0, 0,   0, 1,   1, 0,// 左上角的三角形纹理坐标
     *          0, 1,   1, 0,   1, 1 // 右下角的三角形纹理坐标
     *      }
     * </pre>
     */
    private FloatBuffer textureBuffer = BufferUtil.wrap(new float[]{
            0, 0,   0, 1,   1, 0,
            0, 1,   1, 0,   1, 1
    });
    //"镜像对称"纹理坐标 x' = -(x-1)
    private FloatBuffer mirrorTextureBuffer = BufferUtil.wrap(new float[]{
            1, 0, 	1, 1, 	0, 0,
            1, 1, 	0, 0, 	0, 1
    });
    private float mAngleX;
    private float mAngleY;
    private Resources resources;
    protected Context context;


    public GameModel(Context context){
        resources = context.getResources();
        this.context = context;
    }


    public void setAngleX(float angleX) {
        this.mAngleX = angleX;
    }

    public void setAngleY(float angleY) {
        this.mAngleY = angleY;
    }

    public void drawModel(GL10 gl) {
        drawBody(gl, body.top, textures[index_body_top]);
        drawBody(gl, body.bottom, textures[index_body_bottom]);
        drawBody(gl, body.left, textures[index_body_left]);
        drawBody(gl, body.right, textures[index_body_right]);
        drawBody(gl, body.front, textures[index_body_front]);
        drawBody(gl, body.back, textures[index_body_back]);

        drawFace(gl, head.top, textures[index_head_top]);
        drawFace(gl, head.bottom, textures[index_head_bottom]);
        drawFace(gl, head.left, textures[index_head_left]);
        drawFace(gl, head.right, textures[index_head_right]);
        drawFace(gl, head.front, textures[index_head_front]);
        drawFace(gl, head.back, textures[index_head_back]);

        drawFace(gl, cap.top, textures[index_cap_top]);
        drawFace(gl, cap.bottom, textures[index_cap_bottom]);
        drawFace(gl, cap.left, textures[index_cap_left]);
        drawFace(gl, cap.right, textures[index_cap_right]);
        drawFace(gl, cap.front, textures[index_cap_front]);
        drawFace(gl, cap.back, textures[index_cap_back]);

        drawLeftArm(gl, left_arm.top, textures[index_left_arm_top]);
        drawLeftArm(gl, left_arm.bottom, textures[index_left_arm_bottom]);
        drawLeftArm(gl, left_arm.left, textures[index_left_arm_left]);
        drawLeftArm(gl, left_arm.right, textures[index_left_arm_right]);
        drawLeftArm(gl, left_arm.front, textures[index_left_arm_front]);
        drawLeftArm(gl, left_arm.back, textures[index_left_arm_back]);

        drawRightArm(gl, right_arm.top, textures[index_left_arm_top]);
        drawRightArm(gl, right_arm.bottom, textures[index_left_arm_bottom]);
        //右臂左边绘制左臂右边的纹理, 下同
        drawRightArm(gl, right_arm.left, textures[index_left_arm_right]);
        drawRightArm(gl, right_arm.right, textures[index_left_arm_left]);
        drawRightArm(gl, right_arm.front, textures[index_left_arm_front]);
        drawRightArm(gl, right_arm.back, textures[index_left_arm_back]);

        drawLeftLeg(gl, left_leg.top, textures[index_left_leg_top]);
        drawLeftLeg(gl, left_leg.bottom, textures[index_left_leg_bottom]);
        drawLeftLeg(gl, left_leg.left, textures[index_left_leg_left]);
        drawLeftLeg(gl, left_leg.right, textures[index_left_leg_right]);
        drawLeftLeg(gl, left_leg.front, textures[index_left_leg_front]);
        drawLeftLeg(gl, left_leg.back, textures[index_left_leg_back]);

        drawRightLeg(gl, right_leg.top, textures[index_left_leg_top]);
        drawRightLeg(gl, right_leg.bottom, textures[index_left_leg_bottom]);
        drawRightLeg(gl, right_leg.left, textures[index_left_leg_right]);
        drawRightLeg(gl, right_leg.right, textures[index_left_leg_left]);
        drawRightLeg(gl, right_leg.front, textures[index_left_leg_front]);
        drawRightLeg(gl, right_leg.back, textures[index_left_leg_back]);
    }

    public void loadTexture(GL10 gl) {
        AssetManager assetManager = context.getAssets();
        try {
            InputStream open = assetManager.open("skin/char-28.png");
            textures = new SkinCrop64x32(BitmapFactory.decodeStream(open)).genTextures(gl);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (BitmapSizeUnexpectedException e) {
            e.printStackTrace();
        }
    }

    public void updateAnimation(){
        head_rotate_x += 0.2;
        head_rotate_y += 0.1;
        arm_rotate_x += 1;
    }

    private float[] m = new float[16];
    private void drawFace(GL10 gl, FloatBuffer vertex, int textureID) {
        gl.glLoadIdentity();
        // 注意:矩阵变换的执行顺序从下往上,也就是栈的存储方式
        Matrix.setIdentityM(m, 0);
        Matrix.translateM(m, 0, 0, 0, zOffSet);//最后按向量(0, 0, -2)平移
        touchRotate();
        Matrix.translateM(m, 0, 0, head_height + body_height, 0);
        Matrix.rotateM(m, 0, f(head_rotate_x), 1, 0, 0);
        Matrix.translateM(m, 0, 0, -(head_height + body_height), 0);
        Matrix.rotateM(m, 0, f(head_rotate_y), 0, 1, 0);
        gl.glLoadMatrixf(m, 0);

        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertex);
        gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textureID);
        gl.glDrawElements(GL10.GL_TRIANGLES, vertex_index.remaining(), GL10.GL_UNSIGNED_BYTE, vertex_index);
    }

    private void drawLeftArm(GL10 gl, FloatBuffer vertex, int textureID) {
        gl.glLoadIdentity();
        Matrix.setIdentityM(m, 0);
        Matrix.translateM(m, 0, 0, 0, zOffSet);
        touchRotate();
        Matrix.translateM(m, 0, 0, arm_height / 2, 0);
        Matrix.rotateM(m, 0, g(arm_rotate_x), 1, 0, 0);
        Matrix.translateM(m, 0, 0, -arm_height / 2, 0);
        gl.glLoadMatrixf(m, 0);

        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertex);
        gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textureID);
        gl.glDrawElements(GL10.GL_TRIANGLES, vertex_index.remaining(), GL10.GL_UNSIGNED_BYTE, vertex_index);
    }

    private void drawRightArm(GL10 gl, FloatBuffer vertex, int textureID) {
        gl.glLoadIdentity();
        Matrix.setIdentityM(m, 0);
        Matrix.translateM(m, 0, 0, 0, zOffSet);
        touchRotate();
        Matrix.translateM(m, 0, 0, arm_height / 2, 0);
        Matrix.rotateM(m, 0, -g(arm_rotate_x), 1, 0, 0);
        Matrix.translateM(m, 0, 0, -arm_height / 2, 0);
        gl.glLoadMatrixf(m, 0);

        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertex);
        gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mirrorTextureBuffer);
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textureID);
        gl.glDrawElements(GL10.GL_TRIANGLES, vertex_index.remaining(), GL10.GL_UNSIGNED_BYTE, vertex_index);
    }

    private void drawBody(GL10 gl, FloatBuffer vertex, int textureID) {
        gl.glLoadIdentity();
        Matrix.setIdentityM(m, 0);
        Matrix.translateM(m, 0, 0, 0, zOffSet);
        touchRotate();
        gl.glLoadMatrixf(m, 0);

        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertex);
        gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textureID);
        gl.glDrawElements(GL10.GL_TRIANGLES, vertex_index.remaining(), GL10.GL_UNSIGNED_BYTE, vertex_index);
    }

    private void drawLeftLeg(GL10 gl, FloatBuffer vertex, int textureID) {
        gl.glLoadIdentity();
        Matrix.setIdentityM(m, 0);
        Matrix.translateM(m, 0, 0, 0, zOffSet);
        touchRotate();
        Matrix.translateM(m, 0, 0, -body_height, 0);
        Matrix.rotateM(m, 0, -g(arm_rotate_x), 1, 0, 0);
        Matrix.translateM(m, 0, 0, body_height, 0);
        gl.glLoadMatrixf(m, 0);

        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertex);
        gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textureID);
        gl.glDrawElements(GL10.GL_TRIANGLES, vertex_index.remaining(), GL10.GL_UNSIGNED_BYTE, vertex_index);
    }

    private void drawRightLeg(GL10 gl, FloatBuffer vertex, int textureID) {
        gl.glLoadIdentity();
        Matrix.setIdentityM(m, 0);
        Matrix.translateM(m, 0, 0, 0, zOffSet);
        touchRotate();
        Matrix.translateM(m, 0, 0, -body_height, 0);
        Matrix.rotateM(m, 0, g(arm_rotate_x), 1, 0, 0);
        Matrix.translateM(m, 0, 0, body_height, 0);
        gl.glLoadMatrixf(m, 0);

        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertex);
        gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mirrorTextureBuffer);
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textureID);
        gl.glDrawElements(GL10.GL_TRIANGLES, vertex_index.remaining(), GL10.GL_UNSIGNED_BYTE, vertex_index);

    }

    private void touchRotate() {
        Matrix.rotateM(m, 0, mAngleX, 0, 1, 0);//横向转动
        float z = (float) Math.sin(Math.toRadians(mAngleX));
        float x = (float) Math.cos(Math.toRadians(mAngleX));
        /**
         * 绕Oxz平面的轴旋转,方程:
         *        x           z
         *     -------  =  -------
         *      cos(a)      sin(a)
         *  注意:tan90° = +∞,因此不适合作此方程的系数,选用等价替换 sin(a)/cos(a)
         */
        Matrix.rotateM(m, 0, mAngleY, x, 0, z);//纵向转动
    }

    private float head_rotate_x;
    private float head_rotate_y;
    //头部扭动
    private float f(float x){
        x = x%40 - 10;// [-10, 30], 周期40
        // 函数 f(x) = -|x| 右移10,上移10
        return -Math.abs(x-10)+10;
    }

    private float arm_rotate_x;
    // 四肢的摆动函数
    private float g(float x){
        x = x%80 - 20;
        return -Math.abs(x-20)+20;
    }
}
